/* Copyright (C) 2011 clueless <clueless.mmopeon@gmail.com>
 *
 * This file is part of MMO Peon.
 *
 * MMO Peon is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MMO Peon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MMO Peon.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <windows.h>
#include <cstdint>
#include <sstream>
#include <ctime>
#include "hooks.h"
#include "log.h"
#include "packets.h"

// FIXME: It's possible to cause a crash by placing the hooks while
//        an instruction is being executed in another thread
//        probility is fairly low

// First instruction to be executed after the stub
extern "C" const uintptr_t originalSendFunction = sendHookAddress + 7;
extern "C" const uintptr_t originalRecvFunction = recvHookAddress + 8;

extern "C" void __stdcall
SendHook(struct PktWrapper *packet, uint32_t size)
{
    std::ostringstream out;
    out << "SendHook(" << packet << ", " << size << std::endl << HexDump((uint8_t *)packet, size);
    LogWrite(LOG_PACKET, out.str());

    if (packet->size != (size - 0x0C))
    {
        out.str("");
        out << "Sending packet of " << size
            << " bytes but the packet header says it is " << packet->size
            << " bytes, can't process packet";
        LogWrite(LOG_NORMAL, out.str());
    }
    else
    {
        ParseSendPacket(packet);
    }

}

extern "C" void __stdcall
RecvHook(struct PktWrapper *packet, uint32_t size)
{
    std::ostringstream out;
    out << "RecvHook(" << packet << ", " << size << std::endl << HexDump((uint8_t *)packet, size);
    LogWrite(LOG_PACKET, out.str());
    if (packet->size != (size - 0x0C))
    {
        out.str("");
        out << "Receiving packet of " << size
            << " bytes but the packet header says it is " << packet->size
            << " bytes, can't process packet";
        LogWrite(LOG_NORMAL, out.str());
    }
    else
    {
        ParseRecvPacket(packet);
    }
}

void InstallHooks(void)
{
    uint8_t *jmpbyte;
    uintptr_t *jmpaddr;
    DWORD oldProtect;

    // send hook
    jmpbyte = (uint8_t *)sendHookAddress;
    jmpaddr = (uintptr_t *)(sendHookAddress + 1);
    VirtualProtect((void *)sendHookAddress, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    *jmpbyte = '\xE9';
    *jmpaddr = (uintptr_t)(&SendHookStub) - (sendHookAddress + 5);
    VirtualProtect((void *)sendHookAddress, 5, oldProtect, &oldProtect);

    // recv hook
    jmpbyte = (uint8_t *)recvHookAddress;
    jmpaddr = (uintptr_t *)(recvHookAddress + 1);
    VirtualProtect((void *)recvHookAddress, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    *jmpbyte = '\xE9';
    *jmpaddr = (uintptr_t)(&RecvHookStub) - (recvHookAddress + 5);
    VirtualProtect((void *)recvHookAddress, 5, oldProtect, &oldProtect);
}

void RemoveHooks(void)
{
    DWORD oldProtect;

    // send hook
    VirtualProtect((void *)sendHookAddress, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    memcpy((void *)sendHookAddress, sendHookOriginal, 5);
    VirtualProtect((void *)sendHookAddress, 5, oldProtect, &oldProtect);

    // recv hook
    VirtualProtect((void *)recvHookAddress, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    memcpy((void *)recvHookAddress, recvHookOriginal, 5);
    VirtualProtect((void *)recvHookAddress, 5, oldProtect, &oldProtect);
}
